package com.atguigu.test.proxy;

import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

/**
 * @author zp
 * @version 1.0
 * @description: 说明springAOP的底层实现
 * 一、在spring当中有两个切面的概念：
 * aspect：可以包含多个切点和通知 也是我们使用springAOP当中最常用的方式
 * advisor：只包含一个切点和一个通知 是spring里面底层的实现aop切点的方式
 *
 * 二、springAop实现动态代理的选择（proxyTargetClass默认是false）：
 * 1、proxyTargetClass=false 对象有实现的接口 使用JDK动态代理
 * 2、proxyTargetClass=false 对象没有实现接口 使用CGLIB动态代理
 * 3、proxyTargetClass=true 对象默认使用CGLIB动态代理
 *
 * 三、在分析了Spring AOP的启动过程，总结一下总体过程：
 * 1.setBeanFacotry 接口，在容器启动的时候，创建了 BeanFactoryAdvisorRetrievalHelperAdapter
 * 增强器探索更具和BeanFactoryAspectJAdvisorsBuilderAdapter 增强器的构建器
 * 2.postProcessBeforeInstantiation 接口： 查找所有的切面和Advisor，并将切面的通知解析，
 * 构建成初步的增强器，加入到缓存中来。
 * 3.postProcessAfterInitialization 接口，从缓存取出所有的将所有的增强器，创建代理工厂，
 * 并织入增强器，创建代理对象
 * @date 2022/12/23 19:55
 */
@Slf4j
public class SpringAop {
    @Aspect
    static class Aop{

        @Before("execution(* foo())")
        public void before(){
            log.info("before");
        }

        @After("execution(* foo())")
        public void after(){
            log.info("after");
        }

    }

    public static void main(String[] args) {
        // 创建切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        // 创建通知
        MethodInterceptor methodInterceptor = invocation -> {
            log.info("前置增强");
            Object result = invocation.proceed();
            log.info("后置增强");
            return result;
        };
        // 绑定切点和通知形成切面
        DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor(pointcut,methodInterceptor);
        // 使用代理类工厂 生成代理对象 实现对象的功能的增强
        Target1 target1 = new Target1();
        ProxyFactory proxyFactory = new ProxyFactory();
        // 设置原始对象
        proxyFactory.setTarget(target1);
        // 设置切面
        proxyFactory.addAdvisor(defaultPointcutAdvisor);
        // 设置接口参数
        proxyFactory.setInterfaces(target1.getClass().getInterfaces());
        // 设置proxyTargetClass的值
        proxyFactory.setProxyTargetClass(true);
        // 获取代理对象
        T1 proxy = (T1) proxyFactory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();
    }

    interface T1{
        void foo();
        void bar();
    }

    static class  Target2{

        public void foo() {
            log.info("Target2的foo()方法被调用");
        }

        public void bar() {
            log.info("Target2的bar()方法被调用");
        }
    }

    static class Target1 implements T1{

        @Override
        public void foo() {
            log.info("Target1的foo()方法被调用");
        }

        @Override
        public void bar() {
            log.info("Target1的bar()方法被调用");
        }
    }
}
